#!/usr/bin/python
#
# exp.py - Classes to represent underlying data structures for the grammar
#	 below, for the mini-compiler.
#
# Kurt Schmidt
# 8/07
#
# DESCRIPTION:
#		Just a translation of the C++ implementation by Jeremy Johnson (see
#		programext.cpp)
#
# EDITOR: cols=80, tabstop=2
#
# NOTES
#	environment:
#		a dict
#
#		Procedure calls get their own environment, can not modify enclosing env
#
#	Grammar:
#		program: stmt_list 
#		stmt_list:  stmt ';' stmt_list 
#		    |   stmt  
#		stmt:  assign_stmt 
#		    |  define_stmt 
#		    |  if_stmt 
#		    |  while_stmt 
#		assign_stmt: IDENT ASSIGNOP expr
#		define_stmt: DEFINE IDENT PROC '(' param_list ')' stmt_list END
#		if_stmt: IF expr THEN stmt_list ELSE stmt_list FI
#		while_stmt: WHILE expr DO stmt_list OD
#		param_list: IDENT ',' param_list 
#		    |      IDENT 
#		expr: expr '+' term   
#		    | expr '-' term   
#		    | term            
#		term: term '*' factor   
#		    | factor            
#		factor:     '(' expr ')'  
#		    |       NUMBER 
#		    |       IDENT 
#		    |       funcall 
#		funcall:  IDENT '(' expr_list ')'
#		expr_list: expr ',' expr_list 
#		    |      expr 
#

import sys
import re
from symbolTableObject import * 

####  CONSTANTS   ################

	# the variable name used to store a proc's return value
returnSymbol = 'return'
debugFlag = False
addComments = False
sFileName = "prog.txt"
translateBuffer = ""
iProgramCounter = 0
labelCount = 0
firstNoProc = True
procTranslation = 0

tabstop = '  ' # 2 spaces

######   CLASSES   ##################

class Expr :
	'''Virtual base class for expressions in the language'''

	def __init__( self ) :
		raise NotImplementedError(
			'Expr: pure virtual base class.  Do not instantiate' )

	def eval( self, nt, ft ) :
		'''Given an environment and a function table, evaluates the expression,
		returns the value of the expression (an int in this grammar)'''

		raise NotImplementedError(
			'Expr.eval: virtual method.  Must be overridden.' )

	def translate( self, nt, ft ) :
		'''Given an environment and a function table, tranlates the expression,
		'''

		raise NotImplementedError(
			'Expr.translate: virtual method.  Must be overridden.' )

	def display( self, nt, ft, depth=0 ) :
		'For debugging.'
		raise NotImplementedError(
			'Expr.display: virtual method.  Must be overridden.' )

class Number( Expr ) :
	'''Just integers'''

	def __init__( self, v=0 ) :
		self.value = v
	
	def eval( self, nt, ft ) :
		return self.value

	def translate( self, nt, ft ) :

		global translateBuffer
		
		NUMBER = str(self.value)
		if NUMBER not in nt:
			nt.newConstVar(self.value)

		sendToBuffer("LDA " + NUMBER, "load number from main symbol table")
		#transTxt = "LDI " + NUMBER + "; AC <- memory[" + NUMBER + "]")

		tTempVar = nt.newTempVar()
		if procTranslation > 0:
			macro = Macro()
			macro.ST(tTempVar)
		else :
			sendToBuffer("STA " + tTempVar, "load " + tTempVar)

		return tTempVar

	def display( self, nt, ft, depth=0 ) :
		print "%s%i" % (tabstop*depth, self.value)

class Ident( Expr ) :
	'''Stores the symbol'''

	def __init__( self, name ) :
		self.name = name
	
	def eval( self, nt, ft ) :
		return nt[ self.name ]

	def translate( self, nt, ft ) :
		global translateBuffer
		
		debugMsg("translate Ident")
		if self.name not in nt:
			nt.newVarVar(self.name)
		#else :
			#check type
			#if getattr(nt, self.name).type != "var" :
			#	nt.newVarVar(self.name)	

		if procTranslation > 0:
			macro = Macro()
			macro.LD(self.name)
		else :
			sendToBuffer("LDA " + self.name, "AC <- FP[" + self.name + "]")

		tTempVar  = nt.newTempVar()

		if procTranslation > 0:
			macro = Macro()
			macro.ST(tTempVar)
		else :
			sendToBuffer("STA " + tTempVar, "FP[" + tTempVar + "] = " + self.name)
		
		return tTempVar
		
	def display( self, nt, ft, depth=0 ) :
		print "%s%s" % (tabstop*depth, self.name)


class Times( Expr ) :
	'''expression for binary multiplication'''

	def __init__( self, lhs, rhs ) :
		'''lhs, rhs are Expr's, the operands'''

		# test type here?
		# if type( lhs ) == type( Expr ) :
		self.lhs = lhs
		self.rhs = rhs
	
	def eval( self, nt, ft ) :
		return self.lhs.eval( nt, ft ) * self.rhs.eval( nt, ft )

	def translate( self, nt, ft ) :

		global translateBuffer

		tTempVarL = self.lhs.translate(nt, ft)
		tTempVarR = self.rhs.translate(nt, ft)

		if procTranslation > 0:
			macro = Macro()
			macro.LD(tTempVarL)
			sendToBuffer("STA scratch", "store the FP+offset to FPB")
		else :
			sendToBuffer("LDA " + tTempVarL, "AC <- FP[" + tTempVarL + "]")
			
		if procTranslation > 0:
			macro = Macro()
			macro.LD(tTempVarR)
                        sendToBuffer("MUL scratch", "times FP[" + tTempVarR + "]")
		else :
			sendToBuffer("MUL " + tTempVarR, "multiply " + tTempVarR)
		
		tTempVar  = nt.newTempVar()

		if procTranslation > 0:
			macro = Macro()
			macro.ST(tTempVar)
		else :
			sendToBuffer("STA " + tTempVar, "FP[" + tTempVar + "] = " + tTempVarL)
			
		return tTempVar

	def display( self, nt, ft, depth=0 ) :
		print "%sMULT" % (tabstop*depth)
		self.lhs.display( nt, ft, depth+1 )
		self.rhs.display( nt, ft, depth+1 )
		#print "%s= %i" % (tabstop*depth, self.eval( nt, ft ))


class Plus( Expr ) :
	'''expression for binary addition'''

	def __init__( self, lhs, rhs ) :
		self.lhs = lhs
		self.rhs = rhs
	
	def eval( self, nt, ft ) :
		return self.lhs.eval( nt, ft ) + self.rhs.eval( nt, ft )

	def translate( self, nt, ft ) :
		global translateBuffer
		
		tTempVarL = self.lhs.translate(nt, ft)
		tTempVarR = self.rhs.translate(nt, ft)

		if procTranslation > 0:
			macro = Macro()
			macro.LD(tTempVarL)
			sendToBuffer("STA scratch", "store the FP+offset to FPB")
		else :
			sendToBuffer("LDA " + tTempVarL, "AC <- FP[" + tTempVarL + "]")
			
                if procTranslation > 0:
                        macro = Macro()
                        macro.LD(tTempVarR)
                        sendToBuffer("ADD scratch", "add FP[" + tTempVarR + "]")
                else :
			sendToBuffer("ADD " + tTempVarR, "add " + tTempVarR)
		
		tTempVar  = nt.newTempVar()

		if procTranslation > 0:
			macro = Macro()
			macro.ST(tTempVar)
		else :
			sendToBuffer("STA " + tTempVar, "FP[" + tTempVar + "] = " + tTempVarL)
			
		return tTempVar
		

	def display( self, nt, ft, depth=0 ) :
		print "%sADD" % (tabstop*depth)
		self.lhs.display( nt, ft, depth+1 )
		self.rhs.display( nt, ft, depth+1 )
		#print "%s= %i" % (tabstop*depth, self.eval( nt, ft ))


class Minus( Expr ) :
	'''expression for binary subtraction'''

	def __init__( self, lhs, rhs ) :
		self.lhs = lhs
		self.rhs = rhs
	
	def eval( self, nt, ft ) :
		return self.lhs.eval( nt, ft ) - self.rhs.eval( nt, ft )

	def translate( self, nt, ft ) :

		global translateBuffer
		
		tTempVarL = self.lhs.translate(nt, ft)
		tTempVarR = self.rhs.translate(nt, ft)


		if procTranslation > 0:
			macro = Macro()
			macro.LD(tTempVarR)
			sendToBuffer("STA scratch", "store the FP+offset to FPB")
			macro.LD(tTempVarL)
                        sendToBuffer("SUB scratch", "sub FP[" + tTempVarR + "]")
		else :
			sendToBuffer("LDA " + tTempVarL, "AC <- FP[" + tTempVarL + "]")
			sendToBuffer("SUB " + tTempVarR, "subtract " + tTempVarR)

		tTempVar  = nt.newTempVar()
		if procTranslation > 0:
			macro = Macro()
			macro.ST(tTempVar)
		else :
			sendToBuffer("STA " + tTempVar, "FP[" + tTempVar + "] = " + tTempVarL)

		return tTempVar

	def display( self, nt, ft, depth=0 ) :
		print "%sSUB" % (tabstop*depth)
		self.lhs.display( nt, ft, depth+1 )
		self.rhs.display( nt, ft, depth+1 )
		#print "%s= %i" % (tabstop*depth, self.eval( nt, ft ))


class FunCall( Expr ) :
	'''stores a function call:
	  - its name, and arguments'''
	
	def __init__( self, name, argList ) :
		self.name = name
		self.argList = argList
	
	def eval( self, nt, ft ) :
		return ft[ self.name ].apply( nt, ft, self.argList )

	def translate( self, nt, ft ) :
		''' link for this should happen in the caller's env context'''

		#print "Translate FunCall"
		#print "Symbol Table"
		#nt.printSymbolTable()
		#print "####################"
		if "1" not in nt:
		       nt.newConstVar(1)

		if "2" not in nt:
		        nt.newConstVar(2)
			
		'''translate all args in the caller's FP and SP context'''
		argTempList = []
		for s in self.argList :
			# translate the arg
			argSym = s.translate(nt, ft)
			argTempList.append(argSym)

		''' update to callee's FP and SP context '''
		''' Store FP to scratch '''
		sendToBuffer("LDA FP", "AC <- memory[FP]")
		sendToBuffer("STA scratch", "memory[scratch] = FP")

		''' Store new FP = SP + 1'''
		sendToBuffer("LDA SP", "AC <- memory[SP]")
		sendToBuffer("ADD 1", "Add 1 to SP")
		sendToBuffer("STA FP", "memory[FP] = SP+1")

		''' Determine offset of activation record '''
		method = ft[self.name.upper()]
		tempTable = method.pSymbolTable
		arSize = tempTable.getFrameLength()
	        arSizeStr = str(arSize)

		for findex in range(arSize+1):
	        	findexStr = str(findex)
			if findexStr not in nt:
			        nt.newConstVar(findex)
			
		''' Store new SP '''
		sendToBuffer("ADD " + arSizeStr, "Add arSize to FP")
		sendToBuffer("STA SP", "memory[SP] = FP+arSize")

		''' Store previous FP to new SP - 1'''
		sendToBuffer("SUB 1", "Sub 1 from SP to get prevFP slot")
		sendToBuffer("STA FPB", "Store current mem loc to FPB")
		sendToBuffer("LDA scratch", "Load scratch(prev FP) value to mem")
		sendToBuffer("STI FPB", "Store scratch val to mem loc FPB points at")

		''' Store parameters in activation record '''
		index = 0
		paramCount = 0
		for argSym in argTempList :
			paramCount = paramCount + 1
			# load the prev. FP 
			sendToBuffer("LDA SP", "load SP to AC")
			sendToBuffer("SUB 1", "sub 1 to get the addr of the prev. FP")
			sendToBuffer("STA FPB", "store the addr of the prev. FP to FPB")
			# load the arg in the caller
			if procTranslation > 0:
			        print "procTrans if body: " + self.name.upper()
			        sendToBuffer("LDI FPB", "indirectly load the prev. FP")
				sendToBuffer("ADD " + argSym) #getattr(nt, argSym).addr)
				sendToBuffer("STA FPB", "store the addr of arg to FPB")
				sendToBuffer("LDI FPB", "indirectly load the arg")
				sendToBuffer("STA scratch", "store the arg to scratch")
			else :
			        print "procTrans else body: " + self.name.upper()
			        sendToBuffer("LDI FPB", "indirectly load the prev. FP")
				sendToBuffer("ADD " + argSym + "", "Add var as offset to prev. FP")
				sendToBuffer("STA FPB", "store the addr of arg to FPB")
				sendToBuffer("LDA FPB", "indirectly load the arg")
				sendToBuffer("STA scratch", "store the arg to scratch")

			# store the arg to the callee
			sendToBuffer("LDA FP", "load FP") # load current FP

			indexStr = str(index)
			if indexStr not in nt:
				nt.newConstVar(index)

			sendToBuffer("ADD " + indexStr, "add offset to FP")
			sendToBuffer("STA FPB", "Save FP+offset to FPB")
			sendToBuffer("LDA scratch", "load the arg")
			sendToBuffer("STI FPB", "Save arg to FP+offset")

			index = index + 1		
			
		''' Store Return Address '''
		sendToBuffer("LDA SP", "Load SP into memory")
		#sendToBuffer("SUB 1", "Substract 1 from SP to get ret address loc")
		sendToBuffer("STA FPB", "Temp store this address to FPB")
		#print "iprogramCount: " + str(iProgramCounter)
		#retVal = getattr(method.pSymbolTable, "returnAddr")
		
		value = iProgramCounter+4
		nt.newConstVar(value)
		
		#print retVal.value
		sendToBuffer("LDA "+str(value), "Load current address into mem")
		#sendToBuffer("CAL ", "Load current address into memory")
		#sendToBuffer("STI FPB", "Store CAL value to return address loc")	
		sendToBuffer("STI SP", "Store CAL value to return address loc")	
				
		''' JUMP '''
		sendToBuffer("JMP " + self.name.upper() + "", "Jump to " + self.name)
		
		# FP stored in scratch, to get prev. SP = FP - 1 
		sendToBuffer("LDA FP", "Load FP")

		#print "iprogramCount: " + str(iProgramCounter)
		#retVal = getattr(method.pSymbolTable, returnSymbol)
		#print retVal
		#retVal.value = iProgramCounter
		
		sendToBuffer("STA scratch", "store FP ")	

		''' Update FP '''		
		# prevFP stored in SP - 1 
		sendToBuffer("LDA SP", "Load SP")
		sendToBuffer("SUB 1", "Get prev FP loc ")
		sendToBuffer("STA FPB", "Store memory loc at FPB")
		sendToBuffer("LDI FPB", "Load the prev FP val through FPB")	
		sendToBuffer("STA FP", "store prev FP in FP ")

		''' Get return value, and store it to temp var'''
		sendToBuffer("LDA SP", "Load SP")
		sendToBuffer("SUB 2", "Sub 2 from SP to get retVal slot")	
		sendToBuffer("STA FPB", "Temp save to FPB to get value at SP-2")
		sendToBuffer("LDI FPB", "Load the return val through FPB")	

		tTempVar = nt.newTempVar()

		#print procTranslation
		if procTranslation > 0:
		         macro = Macro()
		         macro.ST(tTempVar)
		else :
		         sendToBuffer("STA " + tTempVar + "", "Store retVal in " + tTempVar)
				
		''' Update SP '''		
		# scratch saved to SP
		sendToBuffer("LDA scratch", "Load scratch")
		sendToBuffer("SUB 1", "sub 1 to get the previous SP")
		sendToBuffer("STA SP", "Store scratch value in SP")

		#if method.pSymbolTable.__contains__(returnSymbol) :
		#        print getattr(method.pSymbolTable, returnSymbol).addr
		#	return returnSymbol
		#else :
		#	print "Error:  no return value"
		#	sys.exit( 2 )
		#temp = getattr(nt, tTempVar)
		#print temp.value
		return tTempVar
			
	def display( self, nt, ft, depth=0 ) :
		print "%sFunction Call: %s, args:" % (tabstop*depth, self.name)
		for e in self.argList :
			e.display( nt, ft, depth+1 )


#-------------------------------------------------------

class Stmt :
	'''Virtual base class for statements in the language'''

	def __init__( self ) :
		raise NotImplementedError(
			'Stmt: pure virtual base class.  Do not instantiate' )

	def eval( self, nt, ft ) :
		'''Given an environment and a function table, evaluates the expression,
		returns the value of the expression (an int in this grammar)'''

		raise NotImplementedError(
			'Stmt.eval: virtual method.  Must be overridden.' )

	def translate( self, nt, ft ) :
		'''Given an environment and a function table, translates the expression,
		'''

		raise NotImplementedError(
			'Stmt.translate: virtual method.  Must be overridden.' )

	def display( self, nt, ft, depth=0 ) :
		'For debugging.'
		raise NotImplementedError(
			'Stmt.display: virtual method.  Must be overridden.' )


class AssignStmt( Stmt ) :
	'''adds/modifies symbol in the current context'''

	def __init__( self, name, rhs ) :
		'''stores the symbol for the l-val, and the expressions which is the
		rhs'''
		self.name = name
		self.rhs = rhs
	
	def eval( self, nt, ft ) :
		nt[ self.name ] = self.rhs.eval( nt, ft )

	def translate( self, nt, ft ) :
		global translateBuffer
		
		if self.name not in nt:
			nt.newVarVar(self.name)
		#else :
			#check type
			#if getattr(nt, self.name).type != "var" :
			#	nt.newVarVar(self.name)
			
		rhand = self.rhs.translate(nt, ft)

		if procTranslation > 0:
			macro = Macro()
			macro.LD(rhand)
			macro = Macro()
			macro.ST(self.name)
		else :
			sendToBuffer("LDA " + rhand, "AC <- FP[" + rhand + "]")
			sendToBuffer("STA " + self.name, "FP[" + self.name + "] = " + rhand)
			
		return self.name

	def display( self, nt, ft, depth=0 ) :
		print "%sAssign: %s :=" % (tabstop*depth, self.name)
		self.rhs.display( nt, ft, depth+1 )


class DefineStmt( Stmt ) :
	'''Binds a proc object to a name'''

	def __init__( self, name, proc ) :
		self.name = name
		self.proc = proc

	def eval( self, nt, ft ) :
		ft[ self.name ] = self.proc

	def translate( self, nt, ft ) :
		global translateBuffer
		
		debugMsg( "translate Define Statement" )
		ft[self.name.upper()] = self.proc
		sendToBuffer(self.name.upper() + ": ",None,False)
		self.proc.translate(nt, ft)
		
	def display( self, nt, ft, depth=0 ) :
		print "%sDEFINE %s :" % (tabstop*depth, self.name)
		self.proc.display( nt, ft, depth+1 )


class IfStmt( Stmt ) :

	def __init__( self, cond, tBody, fBody ) :
		'''expects:
		cond - expression (integer)
		tBody - StmtList
		fBody - StmtList'''
		
		self.cond = cond
		self.tBody = tBody
		self.fBody = fBody

	def translate( self, nt, ft ) :
		debugMsg( "translate if statement" )

		tLabelVar  = newLabelVar()
		tLabelVar2 = newLabelVar()
		tCondVar   = self.cond.translate(nt, ft)

		if procTranslation > 0:
			macro = Macro()
			macro.LD(tCondVar)
		else :
			sendToBuffer("LDA " + tCondVar, "AC <- memory[" + tCondVar + "]")

		sendToBuffer("JMN " + tLabelVar, "if (" + tCondVar + " < 0) goto " + tLabelVar)
		sendToBuffer("JMZ " + tLabelVar, "if (" + tCondVar + " = 0) goto " + tLabelVar)

		self.tBody.translate(nt, ft)
		sendToBuffer("JMP " + tLabelVar2, "goto " + tLabelVar2)
		sendToBuffer(tLabelVar + ": ",None,False)

		self.fBody.translate(nt, ft)
		sendToBuffer(tLabelVar2 + ": ",None,False)

	def display( self, nt, ft, depth=0 ) :
		print "%sIF" % (tabstop*depth)
		self.cond.display( nt, ft, depth+1 )
		print "%sTHEN" % (tabstop*depth)
		self.tBody.display( nt, ft, depth+1 )
		print "%sELSE" % (tabstop*depth)
		self.fBody.display( nt, ft, depth+1 )


class WhileStmt( Stmt ) :

	def __init__( self, cond, body ) :
		self.cond = cond
		self.body = body

	def eval( self, nt, ft ) :
		while self.cond.eval( nt, ft ) > 0 :
			self.body.eval( nt, ft )

	def translate( self, nt, ft ) :

		tLabelVar  = newLabelVar()
		tLabelVar2 = newLabelVar()
		
		sendToBuffer(tLabelVar + ": ",None,False)

		self.cond.translate(nt, ft)
		sendToBuffer("JMN " + tLabelVar2, "goto " + tLabelVar2)
		sendToBuffer("JMZ " + tLabelVar2, "goto " + tLabelVar2)

		self.body.translate(nt, ft)
		sendToBuffer("JMP " + tLabelVar, "goto " + tLabelVar)
		sendToBuffer(tLabelVar2 + ": ",None,False)

		return tLabelVar

	def display( self, nt, ft, depth=0 ) :
		print "%sWHILE" % (tabstop*depth)
		self.cond.display( nt, ft, depth+1 )
		print "%sDO" % (tabstop*depth)
		self.body.display( nt, ft, depth+1 )

#-------------------------------------------------------

class StmtList :
	'''builds/stores a list of Stmts'''

	def __init__( self ) :
		self.sl = []
	
	def insert( self, stmt ) :
		self.sl.insert( 0, stmt )
	
	def eval( self, nt, ft ) :
		for s in self.sl :
			s.eval( nt, ft )
	
	def translate( self, nt, ft ) :
		global firstNoProc
		debugMsg("translate StmtList")
		for s in self.sl :
			if not procTranslation > 0:
				if not isinstance(s, DefineStmt) :
					if firstNoProc :
						firstNoProc = False
						sendToBuffer("MAIN: ", None, False)
			s.translate( nt, ft )

	def display( self, nt, ft, depth=0 ) :
		print "%sSTMT LIST" % (tabstop*depth)
		for s in self.sl :
			s.display( nt, ft, depth+1 )


class Proc :
	'''stores a procedure (formal params, and the body)

	Note that, while each function gets its own environment, we decided not to
	allow side-effects, so, no access to any outer contexts.  Thus, nesting
	functions is legal, but no different than defining them all in the global
	environment.  Further, all calls are handled the same way, regardless of
	the calling environment (after the actual args are evaluated); the proc
	doesn't need/want/get an outside environment.'''

	def __init__( self, paramList, body ) :
		'''expects a list of formal parameters (variables, as strings), and a
		StmtList'''

		self.parList      = paramList
		self.body         = body
		self.pSymbolTable = SymbolTable()

	def apply( self, nt, ft, args ) :
		newContext = self.pSymbolTable

		# sanity check, # of args
		if len( args ) is not len( self.parList ) :
			print "Param count does not match:"
			sys.exit( 1 )

		# bind parameters in new name table (the only things there right now)
			# use zip, bastard
		for i in range( len( args )) :
			item = args[i].translate(nt, ft)
			getattr(newContext, self.parList[i]).value = getattr(nt, item).value

		#print "Function's Symbol table \n"
		#newContext.printSymbolTable()

		# evaluate the function body using the new name table and the old (only)
		# function table.  Note that the proc's return value is stored as
		# 'return in its nametable

		if newContext.__contains__(returnSymbol) :
			return returnSymbol
		else :
			print "Error:  no return value"
			sys.exit( 2 )
		
	def translate( self, nt, ft) :
		global procTranslation

		procTranslation += 1
		# Create a varible in the proc's symbol table for use when
		# function is called
		
		for i in self.parList:
			self.pSymbolTable.newParamVar(i)
			
		self.pSymbolTable.setParentTable(nt)
		self.body.translate(self.pSymbolTable, ft)
		self.pSymbolTable.updateForActivationRecord()
		#self.pSymbolTable.printSymbolTable()
		#print "##################"

		sendToBuffer("LDI SP")
		sendToBuffer("STA scratch")
		sendToBuffer("JMI scratch")
		#sendToBuffer("JMI 184")
		procTranslation -= 1
		#sendToFile(transTxt)
		
		
	def display( self, nt, ft, depth=0 ) :
		print "%sPROC %s :" % (tabstop*depth, str(self.parList))
		self.body.display( nt, ft, depth+1 )


class Program :
	
	def __init__( self, stmtList ) :
		self.stmtList = stmtList
		self.nameTable = {}
		self.funcTable = {}
		self.symbolTable = SymbolTable()
	
	def eval( self ) :
		self.stmtList.eval( self.nameTable, self.funcTable )

	def translate( self ) :
		global outfile, translateBuffer
		debugMsg("translate Program")

		sendToBuffer("JMP MAIN")
		outfile = open(sFileName, 'w')
		self.stmtList.translate( self.symbolTable, self.funcTable )
		sendToBuffer("HLT")

		outfile.write(translateBuffer)
		outfile.close()

	def link ( self, filetoLink, newFileName) :
		debugMsg("Link Program")
		file = open(filetoLink, 'r')
		new_file = open(newFileName,'w')
		lineCount = 0
		instructionMap = {}
		
		for l in file:
			lineCount = lineCount + 1
			if l.find(":") > -1 :
				label = l[0:l.find(":")]
				instructionMap[label] = lineCount			
		file.close()
		file = open(filetoLink, 'r')
		lineNo = 0
		currMethod = ""
		inFunc = False
		for line in file:
			lineNo = lineNo + 1
			#if lineNo in instructionMap.values() :
			#	for k in instructionMap.keys() :
			#		if instructionMap.get(k) == lineNo :
			#			# Only set currMethod when we're looking at an actual method
			#			if k[0] == "L" and k[1:len(k)].isdigit() :
			#				continue
			#			else :
			#				currMethod = k
			#				break

			semi = line.find(";")
			comment = ""
			# If a line has a comment, remove the comment
			if semi > -1 :
				comment = line[semi:len(line)-1]
				line = line[0:semi] + "\n"
			colonLoc = line.find(":")
			if colonLoc > -1 :
				instruc = line[0:colonLoc]
				if instruc in self.funcTable:
					print "Found Function Definition"
					inFunc = True
					currMethod = instruc
					
				line = line[colonLoc+2:len(line)]
				
			if line.find("JMI") > -1 :
				print "Jumping out of Func"
				inFunc = False
				
			if line.find("HLT")>-1:
				new_file.write("HLT")
			elif line.find("CAL") > -1:
				new_file.write(line)
			elif line.find("FPB") > -1 :
				temp = line.find("FPB")				
				line = line[0:temp] + str(3) +"\n"
				new_file.write(line)
			elif line.find("SP") > -1 :
				temp = line.find("SP")
				instruc = line[0:temp]
				line = instruc + str(1) + "\n"
				new_file.write(line)
			elif line.find("FP") > -1 :
				temp = line.find("FP")
				line = line[0:temp] + str(2) + "\n"
				new_file.write(line)
			elif line.find("scratch") > -1 :
				temp = line.find("scratch")
				line = line[0:temp] + str(4) + "\n"
				new_file.write(line)
			elif line.rfind(" ") == -1 :
				if line.find(":") == -1 :
					new_file.write(line)
			else:
				tempStr = line[0:line.rfind(" ") + 1]
				index = tempStr.find(":")
				if index != -1 :
					tempStr = tempStr[index+2:len(tempStr)]

				factStr = line[line.rfind(" ")+1:len(line)-1]

				if instructionMap.has_key(factStr.upper()):
					new_file.write(tempStr + str(instructionMap[factStr]) + comment + "\n")
				else :
					if inFunc :
						attr = getattr(self.funcTable[currMethod].pSymbolTable, factStr)
						if attr != None :
							if attr.type != "const" :
								# addr in this case is just an offset
								# so load the constant value from main mem table
								factStr = str(attr.addr)

							attr = getattr(self.symbolTable, factStr)
							new_file.write(tempStr + str(attr.addr) + comment + "\n")
					else :
						attr = getattr(self.symbolTable, factStr)
						if attr != None :
							new_file.write(tempStr + str(attr.addr) + comment + "\n")
					#else :
						#attr = getattr(self.funcTable[currMethod].pSymbolTable, factStr)
						#new_file.write(tempStr + str(attr.addr) + comment + "\n")
							
		file.close()
		new_file.close()
		
			
	def compile( self ) :
		debugMsg("Compile Program")

		self.translate()
		for p in self.funcTable :
			#self.funcTable[p].pSymbolTable.updateForActivationRecord()
			print "FUNC TABLE: " + p
			self.funcTable[p].pSymbolTable.printSymbolTable()
			print "#####"
		
		self.symbolTable.assignAddresses()
		#self.symbolTable.printSymbolTable()
		self.link("prog.txt", "linkprog.txt")
		
		
	def dump( self ) :
		print "Dump of Symbol Table"
		print "Name Table"
		for k in self.nameTable :
			print "  %s -> %s " % ( str(k), str(self.nameTable[k]) )
		print "Function Table"
		for k in self.funcTable :
			print "  %s" % str(k)

	def display( self, depth=0 ) :
		print "%sPROGRAM :" % (tabstop*depth)
		self.stmtList.display( self.nameTable, self.funcTable )

def debugMsg(msg):
	if debugFlag == True:
		print msg

outfile = None
def sendToFile(txt):
	"send the translation to the Prog.txt"
	debugMsg(txt)
	if outfile != None:
		outfile.writelines(txt)

def sendToBuffer(txt, comment=None, breakline=True):
	"send the translation to the Prog.txt"
	global translateBuffer, iProgramCounter

	translateBuffer += txt

	if addComments and comment != None:
		translateBuffer += "; " + comment

	if breakline == True:
		translateBuffer += "\n"
		iProgramCounter += 1

def newLabelVarName() :
	global labelCount
	
	labelCount += 1
	return "L" + str(labelCount)

def newLabelVar() :
	'''new and add a label variable in the symbol table
	   return variable symber'''
	tLabelVar = newLabelVarName()
	return tLabelVar
	
#function to implement peephole optimization
def optimise(progFile,newProgFile):
	file = open(progFile)
	new_file = open(newProgFile,'w')
	line_num = 0
	curr_line = None
	prev_line = None
	for line in file:
		comment = None
		line_num = line_num + 1
		if re.search(';',line):
			org_line = re.split(';',line)
			curr_line = org_line[0]
			comment = org_line[1]
		else:
			curr_line = line
		if comment == None:
			comment = "\n"
		if re.search("ST",curr_line):
			st_line = re.split(" ",curr_line)
			
			st_var = st_line[-1]
			prev_line = curr_line
			#print st_var
			#new_file.write(curr_line+"\n")
			new_file.write(curr_line+" ;"+comment)
			#print 'ok'
			#
		elif re.search("LD",curr_line) and prev_line!=None:
			ld_line = re.split(" ",curr_line)
			ld_var = ld_line[-1]
			#new_file.write(prev_line)
			if ld_var == st_var:
				#print 'equality met'
				pass
				
			else:
				#new_file.write(curr_line+"\n")
				new_file.write(curr_line+" ;"+comment)				
			
		else:
			#new_file.write(curr_line+"\n")
			new_file.write(curr_line+" ;"+comment)

# expand Macro
class Macro:
	def LD(self, tTempVar):
		sendToBuffer("LDA FP", "load FP to AC")
		sendToBuffer("ADD " + tTempVar, "get offset for FP[" + tTempVar + "]")
		sendToBuffer("STA FPB", "store the FP+offset to FPB")
		sendToBuffer("LDI FPB", "get FB[" + tTempVar + "]")

	def ST(self, tTempVar):
		sendToBuffer("STA scratch", "Store AC to scratch")
		sendToBuffer("LDA FP", "Load FP to AC")
		sendToBuffer("ADD " + tTempVar, "Get offset for FP[" + tTempVar + "]")
		sendToBuffer("STA FPB", "Store memory loc to FPB")
		sendToBuffer("LDA scratch", "Load scratch back to AC")
		sendToBuffer("STI FPB", "Store the value to " + tTempVar)

	def MUL(self, txt):
		pass

	def ADD(self, tTempVar):
		pass

	def SUB(self, txt):
		pass

		
